/* deadlock.c
 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;

void errExitEN(int errn, char *msg) {
  fprintf(stderr, "%s: %s\n", msg, strerror(errn));
  exit(EXIT_FAILURE);
}

static void *threadFunc1(void *arg) {
  int s;
  for (;;) {
    printf("threadFunc1 try to get mtx1\n");
    s = pthread_mutex_lock(&mtx1);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }
    printf("threadFunc1 got mtx1\n");

    printf("threadFunc1 try to get mtx2\n");
    s = pthread_mutex_lock(&mtx2);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }
    printf("threadFunc1 got mtx2\n");

    printf("threadFunc1: hello\n");
    // sleep(1);

    s = pthread_mutex_unlock(&mtx2);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_unlock");
    }
    printf("threadFunc1 release mtx2\n");

    s = pthread_mutex_unlock(&mtx1);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }
    printf("threadFunc1 release mtx1\n");
  }

  return NULL;
}
static void *threadFunc2(void *arg) {
  int s;
  for (;;) {
    printf("threadFunc---2 try to get mtx2\n");
    s = pthread_mutex_lock(&mtx2);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }
    printf("threadFunc---2 got mtx2\n");

    printf("threadFunc---2 try to get mtx1\n");
    s = pthread_mutex_lock(&mtx1);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock\n");
    }
    printf("threadFunc---2 got mtx1\n");

    printf("threadFunc---2: hello\n");
    // sleep(1);

    s = pthread_mutex_unlock(&mtx1);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_unlock");
    }
    printf("threadFunc---2 release mtx1\n");

    s = pthread_mutex_unlock(&mtx2);
    if (s != 0) {
      errExitEN(s, "pthread_mutex_lock");
    }
    printf("threadFunc---2 release mtx2\n");
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  pthread_t t1, t2;
  int loops, s;

  loops = (argc > 1) ? strtol(argv[1], NULL, 10) : 10000000;

  s = pthread_create(&t1, NULL, threadFunc1, &loops);
  if (s != 0) {
    errExitEN(s, "pthread_create");
  }
  s = pthread_create(&t2, NULL, threadFunc2, &loops);
  if (s != 0) {
    errExitEN(s, "pthread_create");
  }

  s = pthread_join(t1, NULL);
  if (s != 0) {
    errExitEN(s, "pthread_join");
  }
  s = pthread_join(t2, NULL);
  if (s != 0) {
    errExitEN(s, "pthread_join");
  }

  exit(EXIT_SUCCESS);
}